﻿using System;
using System.Collections.Generic;

namespace rehot.core
{
    using SanlieHash;
    internal class HotTypeBox
    {
        internal Dictionary<string, Type> typesmap { get; set; }
        internal Dictionary<string, string> namesmap { get; set; }
        internal Dictionary<string, HotType> hot_types { get; set; }
        public HotTypeBox()
        {
            typesmap = new Dictionary<string, Type>();
            hot_types = new Dictionary<string, HotType>();
            namesmap = new Dictionary<string, string>();
        }
        internal void Lose()
        {
            Clear();
            typesmap = null;
            namesmap = null;
            hot_types = null;
        }
        public void Clear()
        {
            foreach (var hot in hot_types.Values) hot.Dispose();
            if (typesmap != null) typesmap.Clear();
            if (namesmap != null) namesmap.Clear();
            if (hot_types != null) hot_types.Clear();
        }
        public HotType FindHotTypeById(string id)
        {
            if (hot_types.ContainsKey(id)) return hot_types[id];
            if (!typesmap.ContainsKey(id)) return null;
            var hotType = new HotType(typesmap[id]);
            hot_types[id] = hotType;
            return hotType;
        }
        public HotType FindHotType(string name, string _namespace)
        {
            var id = FindId(name, _namespace);
            return FindHotTypeById(id);
        }
        public void Add(string name, Type type)
        {
            name = name.ToLower();
            var hash = FastHash.GetSM3(type.FullName);
            typesmap[hash] = type;
            if (namesmap.ContainsKey(name)) namesmap[name] += ";" + hash;
            else namesmap[name] = hash;
        }
        public string FindId(string name, string _namespace = null)
        {
            var namecode = name.ToLower();
            if (!namesmap.ContainsKey(namecode)) return null;
            if (!namesmap[namecode].Contains(";")) return namesmap[namecode];
            else if (_namespace != null)
                foreach (var _name in namesmap[namecode].Split(';'))
                    if (typesmap[_name].Namespace.Contains(_namespace)) return _name;
            return string.Empty;
        }
        public Type Find(string name, string _namespace = null)
        {
            var id = FindId(name, _namespace);
            if (id != string.Empty) return typesmap[id];
            return null;
        }
        public IEnumerable<string> FindTypesByInterface(string interface_name)
        {
            foreach (var type in typesmap) if (_type_has_interface(type.Value, interface_name)) yield return type.Key;
        }
        public IEnumerable<string> FindTypesByAttribute(HotType attr_type)
        {
            foreach (var type in typesmap) if (_type_has_attr(type.Value, attr_type)) yield return type.Key;
        }
        internal static bool _type_has_interface(Type type, string interface_name)
        {
            if (type == null || string.IsNullOrEmpty(interface_name) || !type.IsClass || type.IsAbstract) return false;
            return type.GetInterface(interface_name) != null;
        }
        internal static bool _type_has_attr(Type type, Type attr_type)
        {
            if (type == null || attr_type == null || !type.IsClass || type.IsAbstract) return false;
            foreach (var attr in type.GetCustomAttributes(true)) if (attr.GetType() == attr_type) return true;
            return false;
        }
        internal static bool _type_has_attr(Type type, HotType attr_type)
        {
            return _type_has_attr(type, attr_type._type);
        }
    }
}
